/*
 * Asterisk -- An open source telephony toolkit.
 *
 * Copyright (C) 1999 - 2005, Digium, Inc.
 *
 * Mark Spencer <markster@digium.com>
 *
 * See http://www.asterisk.org for more information about
 * the Asterisk project. Please do not directly contact
 * any of the maintainers of this project for assistance;
 * the project provides a web site, mailing lists and IRC
 * channels for your use.
 *
 * This program is free software, distributed under the terms of
 * the GNU General Public License Version 2. See the LICENSE file
 * at the top of the source tree.
 */

/*! \file
 *
 * \brief CallerID Generation support 
 *
 * \author Mark Spencer <markster@digium.com> 
 */


#include <sys/time.h>
#include <time.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <pulse/pulseaudio.h>
#include <pulse/simple.h>

#include "ulaw.h"
#include "alaw.h"
#include "callerid.h"


float cid_dr[4], cid_di[4];
float clidsb = 8000.0 / 1200.0;
float sasdr, sasdi;
float casdr1, casdi1, casdr2, casdi2;

#define CALLERID_SPACE	2200.0		/*!< 2200 hz for "0" */
#define CALLERID_MARK	1200.0		/*!< 1200 hz for "1" */
#define SAS_FREQ		 440.0
#define CAS_FREQ1		2130.0
#define CAS_FREQ2		2750.0

#define AST_CALLERID_UNKNOWN	"<unknown>"

static inline void gen_tones(unsigned char *buf, int len, format_t codec, float ddr1, float ddi1, float ddr2, float ddi2, float *cr1, float *ci1, float *cr2, float *ci2)
{
	int x;
	float t;
	for (x = 0; x < len; x++) {
		t = *cr1 * ddr1 - *ci1 * ddi1;
		*ci1 = *cr1 * ddi1 + *ci1 * ddr1;
		*cr1 = t;
		t = 2.0 - (*cr1 * *cr1 + *ci1 * *ci1);
		*cr1 *= t;
		*ci1 *= t; 	
		
		t = *cr2 * ddr2 - *ci2 * ddi2;
		*ci2 = *cr2 * ddi2 + *ci2 * ddr2;
		*cr2 = t;
		t = 2.0 - (*cr2 * *cr2 + *ci2 * *ci2);
		*cr2 *= t;
		*ci2 *= t; 	
		buf[x] = AST_LIN2X((*cr1 + *cr2) * 2048.0);
	}
}

static inline void gen_tone(unsigned char *buf, int len, format_t codec, float ddr1, float ddi1, float *cr1, float *ci1)
{
	int x;
	float t;
	for (x = 0; x < len; x++) {
		t = *cr1 * ddr1 - *ci1 * ddi1;
		*ci1 = *cr1 * ddi1 + *ci1 * ddr1;
		*cr1 = t;
		t = 2.0 - (*cr1 * *cr1 + *ci1 * *ci1);
		*cr1 *= t;
		*ci1 *= t; 	
		buf[x] = AST_LIN2X(*cr1 * 8192.0);
	}
}

int ast_gen_cas(unsigned char *outbuf, int sendsas, int len, format_t codec)
{
	int pos = 0;
	int saslen = 2400;
	float cr1 = 1.0;
	float ci1 = 0.0;
	float cr2 = 1.0;
	float ci2 = 0.0;
	
	if (sendsas) {
		if (len < saslen)
			return -1;
		gen_tone(outbuf, saslen, codec, sasdr, sasdi, &cr1, &ci1);
		len -= saslen;
		pos += saslen;
		cr2 = cr1;
		ci2 = ci1;
	}
	gen_tones(outbuf + pos, len, codec, casdr1, casdi1, casdr2, casdi2, &cr1, &ci1, &cr2, &ci2);
	return 0;
}

/*! \brief Initialize stuff for inverse FFT */
void callerid_init(void)
{	
	cid_dr[0] = cos(CALLERID_SPACE * 2.0 * M_PI / 8000.0);
	cid_di[0] = sin(CALLERID_SPACE * 2.0 * M_PI / 8000.0);
	cid_dr[1] = cos(CALLERID_MARK * 2.0 * M_PI / 8000.0);
	cid_di[1] = sin(CALLERID_MARK * 2.0 * M_PI / 8000.0);
	sasdr = cos(SAS_FREQ * 2.0 * M_PI / 8000.0);
	sasdi = sin(SAS_FREQ * 2.0 * M_PI / 8000.0);
	casdr1 = cos(CAS_FREQ1 * 2.0 * M_PI / 8000.0);
	casdi1 = sin(CAS_FREQ1 * 2.0 * M_PI / 8000.0);
	casdr2 = cos(CAS_FREQ2 * 2.0 * M_PI / 8000.0);
	casdi2 = sin(CAS_FREQ2 * 2.0 * M_PI / 8000.0);
}

static unsigned short calc_crc(unsigned short crc, unsigned char data)
{
	unsigned int i, j, org, dst;
	org = data;
	dst = 0;
	
	for (i = 0; i < CHAR_BIT; i++) {
		org <<= 1;
		dst >>= 1;
		if (org & 0x100) 
			dst |= 0x80;
	}
	data = (unsigned char) dst;
	crc ^= (unsigned int) data << (16 - CHAR_BIT);
	for (j = 0; j < CHAR_BIT; j++) {
		if (crc & 0x8000U)
			crc = (crc << 1) ^ 0x1021U ;
		else
			crc <<= 1 ;
	}
   	return crc;
}

static int ast_strlen_zero(const char *s)
{
	if (!s || (*s == '\0')) {
		return 1;
	}
	return 0;
}

static int callerid_genmsg(char *msg, int size, const char *number, const char *name, int flags)
{
	struct tm tm;
	time_t now;
	char *ptr;
	int res;
	int i, x;
	
	/* Get the time */
	now = time(0);
	localtime_r(&now, &tm);
	
	ptr = msg;
	
	/* Format time and message header */
	res = snprintf(ptr, size, "\001\010%02d%02d%02d%02d", tm.tm_mon + 1,
				   tm.tm_mday, tm.tm_hour, tm.tm_min);
	size -= res;
	ptr += res;
	if (ast_strlen_zero(number) || (flags & CID_UNKNOWN_NUMBER)) {
		/* Indicate number not known */
		res = snprintf(ptr, size, "\004\001O");
		size -= res;
		ptr += res;
	} else if (flags & CID_PRIVATE_NUMBER) {
		/* Indicate number is private */
		res = snprintf(ptr, size, "\004\001P");
		size -= res;
		ptr += res;
	} else {
		/* Send up to 16 digits of number MAX */
		i = strlen(number);
		if (i > 16)
			i = 16;
		res = snprintf(ptr, size, "\002%c", i);
		size -= res;
		ptr += res;
		for (x = 0; x < i; x++)
			ptr[x] = number[x];
		ptr[i] = '\0';
		ptr += i;
		size -= i;
	}
	
	if (ast_strlen_zero(name) || (flags & CID_UNKNOWN_NAME)) {
		/* Indicate name not known */
		res = snprintf(ptr, size, "\010\001O");
		size -= res;
		ptr += res;
	} else if (flags & CID_PRIVATE_NAME) {
		/* Indicate name is private */
		res = snprintf(ptr, size, "\010\001P");
		size -= res;
		ptr += res;
	} else {
		/* Send up to 16 digits of name MAX */
		i = strlen(name);
		if (i > 16)
			i = 16;
		res = snprintf(ptr, size, "\007%c", i);
		size -= res;
		ptr += res;
		for (x = 0; x < i; x++)
			ptr[x] = name[x];
		ptr[i] = '\0';
		ptr += i;
		size -= i;
	}
	return (ptr - msg);
	
}

int callerid_generate(unsigned char *buf, const char *number, const char *name, int flags, int callwaiting, format_t codec)
{
	int bytes = 0;
	int x, sum;
	int len;
	
	/* Initial carriers (real/imaginary) */
	float cr = 1.0;
	float ci = 0.0;
	float scont = 0.0;
	char msg[256];
	len = callerid_genmsg(msg, sizeof(msg), number, name, flags);
	if (callwaiting) {
		ast_gen_cas(buf, 1, 2400 + 680, codec);
		bytes+= 2400 + 680;
		buf+= 2400 + 680;
		printf("PUTTING SAS/CAS");
		/*ast_gen_cas(buf, 1, 2400, codec);
		bytes+= 2400;
		buf+= 2400;*/
		/*wait a short time*/
		/*for (x = 0; x < 1000; x++)
			PUT_BYTE(0x7f);*/
	}
	else {
		/* Wait a half a second */
		for (x = 0; x < 4000; x++)
			PUT_BYTE(0x7f);
		/* Transmit 30 0x55's (looks like a square wave) for channel seizure */
		for (x = 0; x < 30; x++)
			PUT_CLID(0x55);
	} 
	/* Send 150ms of callerid marks */
	for (x = 0; x < 150; x++)
		PUT_CLID_MARKMS;
	/* Send 0x80 indicating MDMF format */
	PUT_CLID(0x80);
	/* Put length of whole message */
	PUT_CLID(len);
	sum = 0x80 + strlen(msg);
	/* Put each character of message and update checksum */
	for (x = 0; x < len; x++) {
		PUT_CLID(msg[x]);
		sum += msg[x];
	}
	/* Send 2's compliment of sum */
	PUT_CLID(256 - (sum & 255));
	
	/* Send 50 more ms of marks */
	for (x = 0; x < 50; x++)
		PUT_CLID_MARKMS;
	
	return bytes;
}

void send_cid(char *phone, char *name, int flags, int callwaiting)
{
	unsigned char buff[32000];
	unsigned char buff2[32000];
	format_t codec;
	pa_simple *pa;
	pa_sample_spec ss;
	int length;
	int error;
	
	codec = AST_FORMAT_ALAW;
	//call this at startup instead
	ast_alaw_init();
	ast_ulaw_init();
	callerid_init();
	
	length = callerid_generate(buff, phone, name, flags, callwaiting, codec);
	
	printf("sending %d bytes", length);

	ss.format = PA_SAMPLE_ALAW;
	ss.channels = 1;
	ss.rate = 8000;
	
	pa = pa_simple_new(
					   NULL,               // Use the default server.
					   "USBB2K-API",           // Our application's name.
					   PA_STREAM_PLAYBACK, // Playback
					   NULL,               // Use the default device.
					   "CID",            // Description of our stream.
					   &ss,                // Our sample format.
					   NULL,               // Use default channel map
					   NULL,               // Use default buffering attributes.
					   NULL               // Ignore error code.
					   );
	
	if (pa_simple_write(pa, buff, (size_t)length, &error) < 0){
		fprintf(stderr, ": pa_simple_write() failed: %s\n", pa_strerror(error));
	}
	if (pa_simple_drain(pa, &error) < 0){
		fprintf(stderr, ": pa_simple_drain() failed: %s\n", pa_strerror(error));
	}
	
	pa_simple_free(pa);
}